#include <stdint.h>
#include "gpio_init.h"
#include <unistd.h>
#include "esp_log.h"
#include "driver/ledc.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"

#include "psm.h"
#include "gpio_init.h"

//#include "ets_sys.h"
#define P_SCK 3
#define P_DOUT 2

#define STACK_SIZE_HX717 2048
#define CHANNEL_START 2
#define BUZZER_DUTY 4000
#define BUZZER_TIMER LEDC_TIMER_1
#define BUZZER_CHANNEL LEDC_CHANNEL_0

static TaskHandle_t xHandle_hx717 = NULL;
static StaticTask_t xTaskBuffer_hx717;
static StackType_t xStack_hx717[STACK_SIZE_HX717];

static const char* TAG = "hx717";
static uint8_t channel_status[4 - CHANNEL_START];
static uint32_t channel_value[4 - CHANNEL_START];
static uint8_t current_channel;
static uint8_t hx717_task_status;

static uint8_t press_status;
static uint16_t press_value;
static uint8_t debounce;
static SemaphoreHandle_t xSemaphoreHandle_hx717;

static uint32_t buzzer_duty;
static TickType_t buzzer_muty_time;

static void buzzer_initialize(void)
{
    ledc_timer_config_t ledc_timer = {
        .duty_resolution = LEDC_TIMER_13_BIT, // resolution of PWM duty
        .freq_hz = 1000,                      // frequency of PWM signal
        .speed_mode = LEDC_LOW_SPEED_MODE,           // timer mode
        .timer_num = LEDC_TIMER_1,            // timer index
        .clk_cfg = LEDC_AUTO_CLK,              // Auto select the source clock
    };
    // Set configuration of timer0 for high speed channels
    ledc_timer_config(&ledc_timer);

    ledc_channel_config_t ledc_channel = {
            .channel = BUZZER_CHANNEL,
            .duty = 0,
            .gpio_num = GPIO_BUZZER,
            .speed_mode = LEDC_LOW_SPEED_MODE,
            .hpoint = 0,
            .timer_sel = BUZZER_TIMER,
    };
    ledc_channel_config(&ledc_channel);
}


static uint32_t HX717_convert(uint8_t channel)
{
    uint8_t i;
    uint32_t timeout = 0;
    uint32_t bcd = 0;
    uint8_t pwm_cnt = 0;
    gpio_set_level(GPIO_P_SCK, 0);
    while (gpio_get_level(GPIO_P_DOUT) == 1) {
        if (timeout++ == 0xFFFFFF) {
            printf("hx71 timeout 1\n");
            break;
        }
        vTaskDelay(10 / portTICK_PERIOD_MS);
    }
    usleep(1);
    for (i = 0; i < 24; i++)
    {
        gpio_set_level(GPIO_P_SCK, 1);
        gpio_set_level(GPIO_P_SCK, 0);
        bcd = bcd << 1;
        if (gpio_get_level(GPIO_P_DOUT) == 1)
            bcd++;
    }

    if (channel == 3)
    {
        pwm_cnt = CHANNEL_START + 1;
    }
    else {
        pwm_cnt = channel + 2;
    }
    for (i = 0;i < pwm_cnt; i++)
    {
        gpio_set_level(GPIO_P_SCK, 1);
        gpio_set_level(GPIO_P_SCK, 0);
    }
    bcd = bcd ^ 0x800000;
    return bcd;
}

static void hx717_task(void* p)
{
    uint32_t tmp_value;
    current_channel = CHANNEL_START;
    buzzer_initialize();
    while (1)
    {
        TickType_t current_time = xTaskGetTickCount();
        if (press_status == 1 && press_value < 14300)
        {
            if (current_time > buzzer_muty_time)
            {
                buzzer_muty_time = current_time + 1000 / portTICK_PERIOD_MS;
                if (buzzer_duty == 0)
                {
                    buzzer_duty = BUZZER_DUTY;
                }
                else
                {
                    buzzer_duty = 0;
                }
                ledc_set_duty(LEDC_LOW_SPEED_MODE, BUZZER_CHANNEL, buzzer_duty);
                ledc_update_duty(LEDC_LOW_SPEED_MODE, BUZZER_CHANNEL);
            }

        }
        else
        {
            ledc_set_duty(LEDC_LOW_SPEED_MODE, BUZZER_CHANNEL, 0);
            ledc_update_duty(LEDC_LOW_SPEED_MODE, BUZZER_CHANNEL);
        }
        tmp_value = HX717_convert(current_channel);
        if (xSemaphoreTake(xSemaphoreHandle_hx717, 10) == pdTRUE)
        {
            channel_value[current_channel - CHANNEL_START] = tmp_value;
            //ESP_LOGI(TAG, "ch: %08x, val: %d\n",channel_value[current_channel - CHANNEL_START],  current_channel);
            uint8_t current_prsee_status = 0;
            switch (current_channel)
            {
            case 0:
            case 1:
                break;
            case 2:
                press_value = tmp_value / 640;
                break;
            case 3:
                if (tmp_value <= 0x00f00000)
                {
                    current_prsee_status = 1;
                }
                if (current_prsee_status != press_status)
                {
                    if (debounce++ == 5)
                    {
                        debounce = 0;
                        press_status = current_prsee_status;
                    }

                }
                else
                {
                    debounce = 0;
                }
                break;
            }
            channel_status[current_channel - CHANNEL_START] = 1;
            current_channel++;
            if (current_channel == 4)
            {
                current_channel = CHANNEL_START;
            }
            xSemaphoreGive(xSemaphoreHandle_hx717);
        }
        if (psm_get_status() == PM_CHRAGING || psm_get_status() == PM_GOTO_SLEEP)
        {
            gpio_set_level(GPIO_ZT_EN, 0);
            hx717_task_status = 1;
            vTaskSuspend(NULL);
        }
        vTaskDelay(20 / portTICK_PERIOD_MS);
    }
}



uint8_t hx717_sensor_is_press(void)
{
    bool ret = false;
    if (xSemaphoreTake(xSemaphoreHandle_hx717, 10) == pdTRUE)
    {
        ret = press_status == 1;
        xSemaphoreGive(xSemaphoreHandle_hx717);
    }
    return ret;
}

uint16_t hx717_sensor_get_press_value(void)
{
    uint16_t ret = 0;
    if (xSemaphoreTake(xSemaphoreHandle_hx717, 10) == pdTRUE)
    {
        ret = press_value;
        xSemaphoreGive(xSemaphoreHandle_hx717);
    }
    return ret;
}

bool hx717_get_value(uint8_t channel, uint32_t* val)
{
    bool ret = false;
    if (channel < CHANNEL_START || channel >= 4 || val == NULL)
    {
        return false;
    }
    if (xSemaphoreTake(xSemaphoreHandle_hx717, 10) == pdTRUE)
    {
        if (channel_status[channel - CHANNEL_START] == 1)
        {
            *val = channel_value[channel - CHANNEL_START];
            channel_status[channel - CHANNEL_START] = 0;
            //ESP_LOGI(TAG, "app read ch:%d, val:%08X\n", channel, *val);
            ret = true;
        }
        xSemaphoreGive(xSemaphoreHandle_hx717);
    }
    return ret;
}

void hx717_init(void)
{
    xSemaphoreHandle_hx717 = xSemaphoreCreateMutex();
    if (xSemaphoreHandle_hx717 == NULL)
    {
        ESP_LOGE(TAG, "Create sem failed\n");
        while (1)
            ;
    }
    // Create the task without using any dynamic memory allocation.
    xHandle_hx717 = xTaskCreateStatic(
        hx717_task,         // Function that implements the task.
        "hx717",                   // Text name for the task.
        STACK_SIZE_HX717,    // Stack size in bytes, not words.
        (void*)NULL,            // Parameter passed into the task.
        tskIDLE_PRIORITY + 1,    // Priority at which the task is created.
        xStack_hx717,        // Array to use as the task's stack.
        &xTaskBuffer_hx717); // Variable to hold the task's data structure.
    if (xHandle_hx717 == NULL)
    {
        ESP_LOGE(TAG, "Create tcpclient task fail\n");
    }
}

void hx717_suspend(void)
{
    gpio_set_level(GPIO_ZT_EN, 0);
    vTaskSuspend(xHandle_hx717);
}

bool hx717_is_suspend(void)
{
    return hx717_task_status == 1;
}